{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tuning Hyperparms\n",
    "\n",
    "We will do a simple example of tuning hyperparameters using sklearn's [model_selection.GridSearchCV](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GridSearchCV.html)\n",
    "\n",
    "## get data\n",
    "\n",
    "We will use the SST-2 (Stanford Sentiment Treebank) data set.\n",
    "\n",
    "The input features are short sentences and the labels are the standard sentiment polarity of:\n",
    "*    0 for negative \n",
    "*    1 for positive."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading and extracting SST...\n",
      "\tCompleted!\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "python3 ./glue_examples/download_glue_data.py --data_dir ./glue_examples//glue_data --tasks SST "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SST-2 train data size: 67349 \n",
      "SST-2 dev data size: 872 \n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>text</th>\n",
       "      <th>label</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>66730</th>\n",
       "      <td>with outtakes in which most of the characters ...</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>29890</th>\n",
       "      <td>enigma is well-made</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>45801</th>\n",
       "      <td>is ) so stoked to make an important film about...</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>29352</th>\n",
       "      <td>the closest thing to the experience of space t...</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>19858</th>\n",
       "      <td>lose their luster</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                                    text  label\n",
       "66730  with outtakes in which most of the characters ...      0\n",
       "29890                               enigma is well-made       1\n",
       "45801  is ) so stoked to make an important film about...      0\n",
       "29352  the closest thing to the experience of space t...      1\n",
       "19858                                 lose their luster       0"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "import math\n",
    "import random\n",
    "import csv\n",
    "import sys\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from sklearn import metrics\n",
    "from sklearn.metrics import classification_report\n",
    "\n",
    "from bert_sklearn import BertClassifier\n",
    "from bert_sklearn import BertRegressor\n",
    "from bert_sklearn import load_model\n",
    "\n",
    "DATADIR = './glue_examples/glue_data'\n",
    "\n",
    "\n",
    "def get_sst_data(train_file=DATADIR + '/SST-2/train.tsv',\n",
    "                 dev_file=DATADIR + '/SST-2/dev.tsv'):\n",
    "    \n",
    "    train = pd.read_csv(train_file, sep='\\t', encoding = 'utf8', keep_default_na=False)\n",
    "    train.columns=['text','label']\n",
    "    print(\"SST-2 train data size: %d \"%(len(train)))\n",
    "    \n",
    "    dev = pd.read_csv(dev_file, sep='\\t', encoding = 'utf8', keep_default_na=False)\n",
    "    dev.columns=['text', 'label']\n",
    "    print(\"SST-2 dev data size: %d \"%(len(dev)))\n",
    "    label_list = np.unique(train['label'])\n",
    "    \n",
    "    return train,dev,label_list\n",
    "\n",
    "train,dev,label_list = get_sst_data()\n",
    "\n",
    "# subsample data for demo\n",
    "train = train.sample(1000, random_state=42)\n",
    "\n",
    "X_train = train['text']\n",
    "y_train = train['label']\n",
    "\n",
    "X_dev = dev['text']\n",
    "y_dev = dev['label']\n",
    "\n",
    "train.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## do  grid search"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Suppose we want to tune over some the hyperparameters mentioned in the paper:\n",
    "\n",
    "* **`epochs`** in  [3, 4]\n",
    "\n",
    "\n",
    "* **`learning rate`** in  [2e-5, 3e-5, 5e-5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Building sklearn text classifier...\n",
      "Fitting 3 folds for each of 6 candidates, totalling 18 fits\n",
      "Building sklearn text classifier...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/root/miniconda3/lib/python3.6/site-packages/sklearn/model_selection/_split.py:2053: FutureWarning: You should specify a value for 'cv' instead of relying on the default value. The default value will change from 3 to 5 in version 0.22.\n",
      "  warnings.warn(CV_WARNING, FutureWarning)\n",
      "[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 666, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:09<00:00,  2.30it/s, loss=0.704]\n",
      "Training: 100%|██████████| 21/21 [00:09<00:00,  2.48it/s, loss=0.696]\n",
      "Training: 100%|██████████| 21/21 [00:09<00:00,  2.44it/s, loss=0.694]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 667, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:09<00:00,  2.37it/s, loss=0.607]\n",
      "Training: 100%|██████████| 21/21 [00:09<00:00,  2.40it/s, loss=0.169]\n",
      "Training: 100%|██████████| 21/21 [00:09<00:00,  2.43it/s, loss=0.0286]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 667, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:09<00:00,  2.38it/s, loss=0.649]\n",
      "Training: 100%|██████████| 21/21 [00:09<00:00,  2.38it/s, loss=0.254]\n",
      "Training: 100%|██████████| 21/21 [00:09<00:00,  1.99it/s, loss=0.0918]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 666, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:09<00:00,  2.43it/s, loss=0.668]\n",
      "Training: 100%|██████████| 21/21 [00:10<00:00,  1.64it/s, loss=0.524]\n",
      "Training: 100%|██████████| 21/21 [00:10<00:00,  2.05it/s, loss=0.212]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 667, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:09<00:00,  2.19it/s, loss=0.597]\n",
      "Training: 100%|██████████| 21/21 [00:12<00:00,  1.77it/s, loss=0.246]\n",
      "Training: 100%|██████████| 21/21 [00:12<00:00,  1.35it/s, loss=0.0574]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 667, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:10<00:00,  2.01it/s, loss=0.588]\n",
      "Training: 100%|██████████| 21/21 [00:13<00:00,  1.42it/s, loss=0.233]\n",
      "Training: 100%|██████████| 21/21 [00:11<00:00,  1.81it/s, loss=0.0708]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 666, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:11<00:00,  1.71it/s, loss=0.705]\n",
      "Training: 100%|██████████| 21/21 [00:12<00:00,  1.71it/s, loss=0.707]\n",
      "Training: 100%|██████████| 21/21 [00:13<00:00,  1.47it/s, loss=0.695]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 667, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:11<00:00,  1.84it/s, loss=0.585]\n",
      "Training: 100%|██████████| 21/21 [00:13<00:00,  1.52it/s, loss=0.316]\n",
      "Training: 100%|██████████| 21/21 [00:14<00:00,  1.53it/s, loss=0.121]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 667, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:11<00:00,  1.56it/s, loss=0.711]\n",
      "Training: 100%|██████████| 21/21 [00:14<00:00,  1.54it/s, loss=0.46] \n",
      "Training: 100%|██████████| 21/21 [00:14<00:00,  1.35it/s, loss=0.209]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 666, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:12<00:00,  1.68it/s, loss=0.641]\n",
      "Training: 100%|██████████| 21/21 [00:15<00:00,  1.47it/s, loss=0.265]\n",
      "Training: 100%|██████████| 21/21 [00:13<00:00,  1.60it/s, loss=0.0989]\n",
      "Training: 100%|██████████| 21/21 [00:15<00:00,  1.53it/s, loss=0.0437]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 667, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:11<00:00,  1.68it/s, loss=0.614]\n",
      "Training: 100%|██████████| 21/21 [00:16<00:00,  1.41it/s, loss=0.229]\n",
      "Training: 100%|██████████| 21/21 [00:14<00:00,  1.56it/s, loss=0.0683]\n",
      "Training: 100%|██████████| 21/21 [00:15<00:00,  1.33it/s, loss=0.0119]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 667, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:14<00:00,  1.30it/s, loss=0.597]\n",
      "Training: 100%|██████████| 21/21 [00:14<00:00,  1.22it/s, loss=0.416]\n",
      "Training: 100%|██████████| 21/21 [00:17<00:00,  1.40it/s, loss=0.132]\n",
      "Training: 100%|██████████| 21/21 [00:16<00:00,  1.23it/s, loss=0.0791]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 666, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:11<00:00,  1.69it/s, loss=0.693]\n",
      "Training: 100%|██████████| 21/21 [00:17<00:00,  1.21it/s, loss=0.48] \n",
      "Training: 100%|██████████| 21/21 [00:16<00:00,  1.30it/s, loss=0.23] \n",
      "Training: 100%|██████████| 21/21 [00:16<00:00,  1.32it/s, loss=0.0888]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 667, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:11<00:00,  1.72it/s, loss=0.675]\n",
      "Training: 100%|██████████| 21/21 [00:15<00:00,  1.35it/s, loss=0.565]\n",
      "Training: 100%|██████████| 21/21 [00:15<00:00,  1.58it/s, loss=0.254]\n",
      "Training: 100%|██████████| 21/21 [00:17<00:00,  1.15it/s, loss=0.0718]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 667, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:11<00:00,  1.62it/s, loss=0.62] \n",
      "Training: 100%|██████████| 21/21 [00:16<00:00,  1.29it/s, loss=0.251]\n",
      "Training: 100%|██████████| 21/21 [00:13<00:00,  1.47it/s, loss=0.0747]\n",
      "Training: 100%|██████████| 21/21 [00:17<00:00,  1.34it/s, loss=0.0156] \n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 666, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:12<00:00,  1.81it/s, loss=0.693]\n",
      "Training: 100%|██████████| 21/21 [00:16<00:00,  1.31it/s, loss=0.635]\n",
      "Training: 100%|██████████| 21/21 [00:15<00:00,  1.57it/s, loss=0.404]\n",
      "Training: 100%|██████████| 21/21 [00:14<00:00,  1.57it/s, loss=0.233]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 667, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:11<00:00,  1.72it/s, loss=0.617]\n",
      "Training: 100%|██████████| 21/21 [00:15<00:00,  1.37it/s, loss=0.419]\n",
      "Training: 100%|██████████| 21/21 [00:16<00:00,  1.36it/s, loss=0.187]\n",
      "Training: 100%|██████████| 21/21 [00:15<00:00,  1.32it/s, loss=0.0831]\n",
      "                                                           \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 667, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 21/21 [00:13<00:00,  1.52it/s, loss=0.709]\n",
      "Training: 100%|██████████| 21/21 [00:15<00:00,  1.42it/s, loss=0.536]\n",
      "Training: 100%|██████████| 21/21 [00:15<00:00,  1.43it/s, loss=0.192]\n",
      "Training: 100%|██████████| 21/21 [00:15<00:00,  1.44it/s, loss=0.0655]\n",
      "[Parallel(n_jobs=1)]: Done  18 out of  18 | elapsed: 18.4min finished\n",
      "/root/miniconda3/lib/python3.6/site-packages/sklearn/model_selection/_search.py:841: DeprecationWarning: The default of the `iid` parameter will change from True to False in version 0.22 and will be removed in 0.24. This will change numeric results when test-set sizes are unequal.\n",
      "  DeprecationWarning)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Building sklearn text classifier...\n",
      "Loading bert-base-uncased model...\n",
      "Defaulting to linear classifier/regressor\n",
      "train data size: 1000, validation data size: 0\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 32/32 [00:21<00:00,  1.58it/s, loss=0.571]\n",
      "Training: 100%|██████████| 32/32 [00:22<00:00,  1.84it/s, loss=0.248]\n",
      "Training: 100%|██████████| 32/32 [00:25<00:00,  1.66it/s, loss=0.0716]\n",
      "Training: 100%|██████████| 32/32 [00:21<00:00,  1.59it/s, loss=0.0343]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 12min 35s, sys: 7min 3s, total: 19min 39s\n",
      "Wall time: 20min\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "from sklearn.model_selection import GridSearchCV\n",
    "\n",
    "params = {'epochs':[3, 4], 'learning_rate':[2e-5, 3e-5, 5e-5]}\n",
    "\n",
    "# wrap classifier/regressor in GridSearchCV\n",
    "clf = GridSearchCV(BertClassifier(validation_fraction=0,max_seq_length=64), \n",
    "                    params,\n",
    "                    scoring='accuracy',\n",
    "                    verbose=True)\n",
    "\n",
    "# fit gridsearch \n",
    "clf.fit(X_train ,y_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.767 (+/-0.306) for {'epochs': 3, 'learning_rate': 2e-05}\n",
      "0.873 (+/-0.015) for {'epochs': 3, 'learning_rate': 3e-05}\n",
      "0.751 (+/-0.284) for {'epochs': 3, 'learning_rate': 5e-05}\n",
      "0.876 (+/-0.015) for {'epochs': 4, 'learning_rate': 2e-05}\n",
      "0.866 (+/-0.040) for {'epochs': 4, 'learning_rate': 3e-05}\n",
      "0.841 (+/-0.017) for {'epochs': 4, 'learning_rate': 5e-05}\n",
      "\n",
      "Best score: 0.876 with params: {'epochs': 4, 'learning_rate': 2e-05}\n"
     ]
    }
   ],
   "source": [
    "means = clf.cv_results_['mean_test_score']\n",
    "stds = clf.cv_results_['std_test_score']\n",
    "\n",
    "for mean, std, params in zip(means, stds, clf.cv_results_['params']):\n",
    "        print(\"%0.3f (+/-%0.03f) for %r\"\n",
    "              % (mean, std * 2, params))\n",
    "        \n",
    "# best scores\n",
    "print(\"\\nBest score:\", clf.best_score_,\"with params:\", clf.best_params_)        "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
